package org.ltsh.generate.templete;

import com.alibaba.fastjson.JSONObject;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.ltsh.generate.config.GlobalConfig;
import org.ltsh.generate.entity.GenerateEntity;
import org.ltsh.generate.entity.PackageGenerateEntity;
import org.ltsh.generate.utils.DateUtils;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 调用说明
 * 内置参数:className 类名
 * 内置参数:tableName 表名
 * 内置参数:columnDatas 字段数据 示例:
 * colunmData.propertyName 字段名称
 * colunmData.describe 字段注释
 * columnData.dataType 字段类型
 * Created by fengjianbo on 2016/2/15 0015.
 */
public class XmlTempleteUtils {

    public static final String PRO_REGEX="\\@\\{[^}]*\\}";

    /**
     *
     * @throws Exception
     */
    public static void writeFile(GenerateEntity entity) throws Exception{

        XmlTempleteUtils templeteUtils = new XmlTempleteUtils();
//        map.put("packageName", autoEntity.getBasePackageStr());
//        map.put("modulesName", autoEntity.getModulesName());
        entity.setOutPath(GlobalConfig.applicationConfig.getBaseOutPath() + entity.getOutPath());
        entity.setTempletePath(GlobalConfig.applicationConfig.getProjectPath() +"/" + entity.getTempletePath());
        String fileName = entity.getFileName();
        String templeteStr = templeteUtils.getTempleteStr(entity);
        if(entity.getTempleteType().equals("java")) {
            String headerStr = templeteStr.substring(0, templeteStr.indexOf("\n"));
            if(headerStr.contains(";")) {
                headerStr = headerStr.substring(0, headerStr.indexOf(";"));
            }
            headerStr = headerStr.replace("package ", "").trim();
            String pageckagePath = entity.getOutPath() + "/" + headerStr.replaceAll("\\.","/");
            entity.setOutPath(pageckagePath);
        } else {
            entity.setOutPath(entity.getOutPath() + "/" + entity.getModulesName());
        }

//        System.gc();
        System.out.println("生成文件:" + entity.getOutPath() + "\\" + fileName);
        TempleteFileUtils.writeFile(templeteStr, fileName, entity.getOutPath());

    }
    /**
     *
     * @throws Exception
     */
    public static void writeFileForPage(PackageGenerateEntity packageGenerateEntity) throws Exception{
//        autoEntity.setModulesPath(autoEntity.getBasePackageStr().replaceAll("\\.","/") + "/" + autoEntity.getModulesName());
//        GenerateEntity generateEntity = new GenerateEntity();
        String outPath = null;
        String packageName = packageGenerateEntity.getBasePackageStr();
        if(packageGenerateEntity.getModulesName() != null) {
            packageName = "." + packageGenerateEntity.getModulesName();
        }
        outPath = packageGenerateEntity.getOutPath() + "/" + packageName.replaceAll("\\.","/");
        packageGenerateEntity.setOutPath(outPath);
        packageGenerateEntity.getExtraParams().put("packageName", packageName);
        writeFile(packageGenerateEntity);
    }

    public String getTempleteStr(GenerateEntity entity) throws Exception {
        Map<String, Object> params = new HashMap<String, Object>();

        params = JSONObject.parseObject(JSONObject.toJSONString(entity), Map.class);
        if(entity.getExtraParams() != null) {
            params.putAll(entity.getExtraParams());
        }
        params.put("currentDate", DateUtils.format(new Date(), DateUtils.YYYY_MM_DD_HH_MM_SS));
        params = getData(entity.getTableName(), params);
        String templetePath = entity.getTempletePath();
        File tempFile = new File(templetePath);
        if(!tempFile.exists()) {
            tempFile.mkdirs();
        }
//        FileReader fileReader = new FileReader(tempFile);
        FileInputStream fileInputStream = new FileInputStream(templetePath);
//        InputStream resourceAsStream = TempleteUtils.class.getResourceAsStream(basePath);
        InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        StringBuffer sb = new StringBuffer();
        String tempStr = null;
        while ((tempStr = bufferedReader.readLine()) != null) {
            sb.append(tempStr);
            sb.append("\n");
        }
        bufferedReader.close();
        inputStreamReader.close();
        fileInputStream.close();
        Map<String, Object> templeteMap = new HashMap<String, Object>();
        String reloadTemplete = reloadTemplete(templeteMap, sb.toString());
        reloadTemplete = replaceTemplete(reloadTemplete, params);
        for (String str : templeteMap.keySet()) {
            String s = (String) templeteMap.get(str);
            String xml = xml(s, params);
            if(xml.trim().length() > 0) {
                reloadTemplete = reloadTemplete.replace(String.format(templeteTemp, str), xml);
            }
        }
        return reloadTemplete;
    }


    public Map<String, Object> getData(String tableName, Map<String, Object> map){
        if(map == null) {
            map = new HashMap<String, Object>();
        }

//        map.put("className", DataUtil.getMethodName(tableName));
//        map.put("lowerClassName", DataUtil.getPropertyName(tableName));
//        map.put("tableName", tableName);

        if(map.get("columnDatas") == null) {
            List<Map<String, Object>> columnDatas = DataUtil.getColumnData(tableName);
            map.put("columnDatas", columnDatas);
        }
        return map;
    }
    /**
     * 替换模板
     * @param templeteStr
     * @param repleaceMap
     * @return
     * @throws Exception
     */
    public static String replaceTemplete(String templeteStr, Map<String, Object> repleaceMap) throws Exception {
        if(templeteStr.trim().length() <=0) {
            return "";
        }
        Pattern compile = Pattern.compile(PRO_REGEX);
        Matcher matcher = compile.matcher(templeteStr);
        while(matcher.find()) {
            String tempStr = matcher.group();
            String tempStr1 = tempStr.substring(2, tempStr.length() - 1);
            String[] split = tempStr.split(".");
            if(split.length > 1) {
                Map<String, Object> tempMap = null;
                for (int i = 0; i < split.length; i++) {
                    Object obj = repleaceMap.get(split[i]);
                    if(obj != null) {
                        if(obj instanceof Map) {
                            tempMap = (Map) obj;
                        }
                    }

                }
                if(tempMap != null) {
                    for (String key : tempMap.keySet()) {

                        templeteStr = templeteStr.replace(tempStr, String.valueOf(tempMap.get(key)));
                    }
                } else {
                    throw new Exception("参数:\"" + tempStr1 + "\"未定义");
                }

            } else {

                templeteStr = templeteStr.replace(tempStr, String.valueOf(repleaceMap.get(tempStr1)));
            }
        }
        return templeteStr;
    }
    public static final String TEMPLETE_REGEX_DELETE="<templeteScript[^>]*>|</templeteScript>";
    public static final String TEMPLETE_REGEX=".*?<templeteScript[^>]*>((.|\\n)*?)<\\/templeteScript>.*?";
    public static final String templeteTemp = "####templeteTemp----------%s####";
    public static String reloadTemplete(Map<String, Object> templeteMap, String templeteStr) {
        if(templeteStr.trim().length() <=0) {
            return "";
        }
        Pattern pattern = Pattern.compile(TEMPLETE_REGEX);
        Matcher matcher = pattern.matcher(templeteStr);
        int inx = 0;
        while(matcher.find()) {
            String group = matcher.group();
            String index = String.format(templeteTemp, inx);
            templeteStr = templeteStr.replace(group, index);
            templeteMap.put(String.valueOf(inx), group);
            inx ++;
        }
        return templeteStr;
    }
    public static String xml(String xmlStr, Map<String, Object> params) throws Exception {
        SAXReader reader = new SAXReader();
        ExpressionParser expressionParser = new SpelExpressionParser();
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        ctx.setVariables(params);
        StringReader stringReader = new StringReader(xmlStr);
        try {
            org.dom4j.Document document = reader.read(stringReader);
            stringReader.close();
            Element rootElement = document.getRootElement();
//            String text = rootElement.getText();
//
//            if(text.trim().length() > 0) {
//                String reloadTemplete = reloadTemplete(params, text);
//                xmlStr = xmlStr.replace(text, reloadTemplete);
//            }
            StringBuffer stringBuffer = new StringBuffer();
            ergodic(stringBuffer, rootElement, params, null, expressionParser);
            String s = stringBuffer.toString();
            if(s.endsWith(",")) {
                s = s.substring(0, s.length() - 1);
            } else if(s.endsWith(",\n")) {
                s = s.substring(0, s.length() - 2);
            } else if(s.endsWith("\n")) {
                s = s.substring(0, s.length() - 1);
            }
            return s;
        } finally {


        }

//        return xmlStr;
    }
    public static void ergodic(StringBuffer stringBuffer,Element element, Map params, String itemName,ExpressionParser expressionParser) throws Exception {
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        ctx.setVariables(params);
        List<Element> elements = element.elements();
//        String text = element.getText();
//        if(text.trim().length() > 0) {
//            String s = replaceTemplete(text, params);
//            s = s == null ? "":s;
//            stringBuffer.append(s);
//        }
        if(elements.size() > 0) {
            for (Element elementTmp : elements) {
                String name = elementTmp.getName();
                if(name.equals("foreach")) {
                    String items = elementTmp.attributeValue("items");
                    String trim = elementTmp.attributeValue("trim");
                    List list = expressionParser.parseExpression(items).getValue(ctx, List.class);
                    for (int i = 0; i < list.size(); i++) {
                        Object obj = list.get(i);
                        Map item = (Map) obj;
                        Map clone = (Map)((HashMap) params).clone();
                        itemName = elementTmp.attributeValue("itemName");
                        itemName = itemName == null ? "item" : itemName;
                        clone.put(itemName, item);
                        String text1 = elementTmp.getText();
                        if(text1.trim().length() > 0) {
                            String s = replaceTemplete(text1, item);
                            s = removeNullLine(s);
                            stringBuffer.append(s);
                            if(!s.endsWith("\n")) {
                                stringBuffer.append("\n");
                            }
//                            if(i < list.size() - 1) {
//                                stringBuffer.append("\n");
//                            }
                        }
                        ergodic(stringBuffer, elementTmp, clone, itemName, expressionParser);
                        if(i == list.size() -1) {
                            if(trim != null && trim.length() > 1) {
                                if(stringBuffer.toString().endsWith(trim)) {
                                    stringBuffer = new StringBuffer(stringBuffer.substring(0, stringBuffer.length()-trim.length()));
                                }
                            }
                        }
                    }

                } else if(name.equals("if")) {
                    String test = elementTmp.attributeValue("test");
                    String text1 = elementTmp.getText();
                    Boolean value = expressionParser.parseExpression(test).getValue(ctx, Boolean.class);
                    if(value) {
                        Map map = new HashMap();
                        map.putAll(params);
                        if(itemName != null && params.get(itemName) instanceof Map) {
                            map.putAll((Map)params.get(itemName));
                        }

                        String s = replaceTemplete(text1, map);
                        s = removeNullLine(s);
                        stringBuffer.append(s);
                    }
                }
            }
        }
    }
    public static String removeNullLine(String s) throws IOException {
        StringBuffer stringBuffer = new StringBuffer();
        StringReader stringReader = new StringReader(s);
        BufferedReader bufferedReader = new BufferedReader(stringReader);
        try {
            String readLine = null;
            while((readLine = bufferedReader.readLine()) != null) {
                if(readLine.trim().length() > 0) {
                    stringBuffer.append(readLine).append("\n");
                }
            }
        } finally {
            bufferedReader.close();
            stringReader.close();
        }
        return stringBuffer.toString();
    }

}
